package org.muninn.niagara.services;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.net.ConnectException;
import java.util.ArrayList;

import javax.baja.alarm.BAlarmTransitionBits;
import javax.baja.alarm.ext.BAlarmSourceExt;
import javax.baja.alarm.ext.BLimitEnable;
import javax.baja.alarm.ext.BOffnormalAlgorithm;
import javax.baja.alarm.ext.offnormal.BBooleanChangeOfStateAlgorithm;
import javax.baja.alarm.ext.offnormal.BBooleanCommandFailureAlgorithm;
import javax.baja.alarm.ext.offnormal.BEnumChangeOfStateAlgorithm;
import javax.baja.alarm.ext.offnormal.BEnumCommandFailureAlgorithm;
import javax.baja.alarm.ext.offnormal.BOutOfRangeAlgorithm;
import javax.baja.control.BEnumPoint;
import javax.baja.fox.BFoxProxySession;
import javax.baja.history.BCapacity;
import javax.baja.history.BHistoryConfig;
import javax.baja.history.BHistoryDevice;
import javax.baja.history.BHistoryId;
import javax.baja.history.BIHistory;
import javax.baja.history.BRolloverValue;
import javax.baja.history.ext.BBooleanCovHistoryExt;
import javax.baja.history.ext.BBooleanIntervalHistoryExt;
import javax.baja.history.ext.BEnumCovHistoryExt;
import javax.baja.history.ext.BEnumIntervalHistoryExt;
import javax.baja.history.ext.BHistoryExt;
import javax.baja.history.ext.BNumericCovHistoryExt;
import javax.baja.history.ext.BNumericIntervalHistoryExt;
import javax.baja.naming.BHost;
import javax.baja.naming.BOrd;
import javax.baja.security.BPassword;
import javax.baja.status.BStatusBoolean;
import javax.baja.status.BStatusEnum;
import javax.baja.sys.Action;
import javax.baja.sys.BComponent;
import javax.baja.sys.BDynamicEnum;
import javax.baja.sys.BEnumRange;
import javax.baja.sys.BFacets;
import javax.baja.sys.BIcon;
import javax.baja.sys.BInteger;
import javax.baja.sys.BRelTime;
import javax.baja.sys.BStation;
import javax.baja.sys.BString;
import javax.baja.sys.Flags;
import javax.baja.sys.Property;
import javax.baja.sys.Sys;
import javax.baja.sys.Type;
import javax.baja.units.BUnit;
import javax.baja.util.BFormat;
import javax.baja.util.BNameList;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;

import com.tridium.fox.session.FoxAuthenticationException;

import com.tridium.history.fox.BFoxHistorySpace;

public class BExtService extends BMuninnService
{
  public static final Property deleteAllExtensions = newProperty(0, false);
  public void setDeleteAllExtensions(boolean v) { setBoolean(deleteAllExtensions, v); }
  public boolean getDeleteAllExtensions() { return getBoolean(deleteAllExtensions); }
  
  /** Path to the file containing ext information for points **/
  public static final Property filePath = newProperty(0, "C:\\Users\\Admin\\Desktop\\TCC.csv");
  public void setFilePath(String v) { setString(filePath, v); }
  public String getFilePath() { return getString(filePath); }
  
  /** target JACE IP */
  public static final Property targetIP = newProperty(0, "192.168.0.20");
  public void setTargetIP(String v) { setString(targetIP, v); }
  public String getTargetIP() { return getString(targetIP); }
  
  /** target JACE port */
  public static final Property port = newProperty(0, "1911");
  public void setPort(String v) { setString(port, v); }
  public String getPort() { return getString(port); }
  
  /** target JACE username */
  public static final Property username = newProperty(0, "admin");
  public void setUsername(String v) { setString(username, v); }
  public String getUsername() { return getString(username); }
  
  /** target JACE password */
  public static final Property password = newProperty(0, BPassword.make(""));
  public void setPassword(BPassword v) { set(password, v); }
  public BPassword getPassword() { return (BPassword) get(password); }
  
  public static final Property trends = newProperty(0, true);
  public void setTrends(boolean v) { setBoolean(trends, v); }
  public boolean getTrends() { return getBoolean(trends); }
  
  public static final Property alarms = newProperty(0, true);
  public void setAlarms(boolean v) { setBoolean(alarms, v); }
  public boolean getAlarms() { return getBoolean(alarms); }
  
  /** Length of time for which to save history records (604800000 ms = one week by default) **/
  public static final Property histPeriod = newProperty(0, BRelTime.make(604800000));
  public void setHistPeriod(BRelTime v) { set(histPeriod, v); }
  public BRelTime getHistPeriod() { return (BRelTime) get(histPeriod); }
  
  /** Format for the source of all exts **/
  public static final Property sourceName = newProperty(0, BFormat.make("%parent.parent.parent.displayName%_%parent.displayName%"));
  public void setSourceName(BFormat v) { set(sourceName, v); }
  public BFormat getSourceName() { return (BFormat) get(sourceName); }
  
  /** Optional text for when a point transitions to normal (alarm exts only) **/
  public static final Property alarmNormalText = newProperty(0, BFormat.make("%parent.displayName% transitioned to normal value %out%"));
  public void setAlarmNormalText(BFormat v) { set(alarmNormalText, v); }
  public BFormat getAlarmNormalText() { return (BFormat) get(alarmNormalText); }
  
  private static BExtService INSTANCE = null;
  
  private static String file;
  private static long logPer;
  private String[] names;
  private boolean doDel;
  private boolean trend, alarm;
  private BHistoryExt[] histExts;
  private BAlarmSourceExt[] alrmExts;
  private BFacets[] facets;
  private NodeList list;
  
  private BFoxProxySession foxProxySession;
  private BStation station;
  
  public static final Action execute = newAction(Flags.ASYNC|Flags.DEFAULT_ON_CLONE, null);
  public void execute() { invoke(execute, null, null); }
  
  public void doExecute() throws Exception
  { 
    System.out.println("------------------------------");
    System.out.println("Executing Muninn ExtService...");
    System.out.println("------------------------------");
    
    trend = getTrends();
    alarm = getAlarms();
    doDel = getDeleteAllExtensions();
    
    if (trend || alarm || doDel)
    {
      System.out.print("Creating XML file... ");
      
      Converter converter = new Converter();
      String csv = getFilePath();
      converter.run(csv);
      
      System.out.println("XML created");
      System.out.println();
      
      String ipAddress = getTargetIP();
      String port = getPort();
      String username = getUsername();
      String password = getPassword().getString();
      
      file = csv.substring(0, csv.lastIndexOf('\\') + 1) + "ExtList.xml";
      logPer = getHistPeriod().getMillis();
      
      try
      {
        foxProxySession = openSession(ipAddress, port, username, password);
        if (foxProxySession == null) return;
        
        System.out.println("Done");
        System.out.println();
        
        station = (BStation) BOrd.make("station:|slot:").get(foxProxySession);
      }
      catch (ConnectException e) { System.out.println("Connection Timed Out: Canceling Service"); return; }
      catch (FoxAuthenticationException e) { System.out.println("Connection Refused: Canceling Service"); return; }
      
      try
      {
        Document doc;
        DocumentBuilder builder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
        doc = builder.parse(file);
        
        System.out.print("Building from: " + file + "... ");
        list = doc.getDocumentElement().getElementsByTagName("point");
      }
      catch (Exception e)
      {
        System.out.println("Invalid file");
        System.out.println(e.toString());
        return;
      }
      
      System.out.println("Done");
      System.out.println();
      
      histExts = new BHistoryExt[list.getLength()];
      alrmExts = new BAlarmSourceExt[list.getLength()];
      names = new String[list.getLength()];
      facets = new BFacets[list.getLength()];
      
      for (int i = 0; i < list.getLength(); i++)
        names[i] = ((Element) list.item(i)).getAttribute("name").toLowerCase();
      
      BComponent comp = BOrd.make("station:|slot:/Drivers").resolve(station).getComponent();
      comp.lease();
      process(comp);
      
      cleanup();
      closeSession(foxProxySession);
    }
    
    else
    {
      System.out.println("All options disabled. Nothing to do.");
    }
    
    System.out.println("------------------------------");
    System.out.println(" Completed Muninn ExtService  ");
    System.out.println("------------------------------");
  }

  private void cleanup()
  {
    //Delete History From Database Here---
    BOrd bord = BOrd.make("history:");  
    BFoxHistorySpace  c = (BFoxHistorySpace) bord.resolve(station).get();
    BHistoryDevice[] devices = c.listDevices();
    for (int i = 0; i < devices.length; i++)
    {
      BIHistory[] histories = c.listHistories(devices[i]);
      for(int j = 0; j < histories.length; j++)
      {
        if(histories[j].getDisplayName(null).indexOf("_cfg0") >= 0)
          c.deleteHistory(histories[j].getId(), null);
      }
    }
    //-------------------------
  }

  public void process(BComponent curComp) throws Exception
  {
    if (curComp.getName().equals("network")) return; //this is necessary on some stations
    curComp.lease();
    
    String type = curComp.getType().toString().toLowerCase();
    String name = curComp.getDisplayName(null).toLowerCase();
    
    if ((type.indexOf("numeric") >= 0 || type.indexOf("enum") >= 0 || type.indexOf("boolean") >= 0) &&
        (type.indexOf("point") >= 0 || type.indexOf("writable") >= 0))
    {
      System.out.println("  Matching: " + name);
      for (int i = 0; i < names.length; i++)
      {
        if (!doDel && nameMatch(names[i], name, 0))
        {
          curComp.removeAll();
          if (trend)
            makeHistExt(curComp, i, type);
          if (alarm)
            makeAlrmExt(curComp, i, type);
          return;
        }

        else if (i == names.length - 1)
          removeExts(curComp);
      }
    }

    else
    {
      BComponent[] comps = curComp.getChildComponents();
      for (int i = 0; i < comps.length; i++)
        process(comps[i]);
    }
  }
  
  private boolean nameMatch(String xmlname, String pointname, int index)
  { 
    String original = pointname;
    boolean prevIsX = false;
    int adjust = 0;

    if (pointname.indexOf(xmlname) == 0)
      return true;

    if(xmlname.indexOf('x') >= 0 && findDigit(pointname))
    {
      for (int i = index; i < pointname.length(); i++)
      {
        if(Character.isDigit(pointname.charAt(i)))
        {
          if (prevIsX)
          {
            pointname = pointname.replaceFirst(pointname.substring(i, i + 1), "x");

            if (pointname.indexOf(xmlname) == 0)
              return true;

            pointname = pointname.replaceFirst(pointname.substring(i, i + 1), "");
            adjust++;
            i--;
          }

          else
          {
            pointname = pointname.replaceFirst(pointname.substring(i, i + 1), "x");
            prevIsX = true;
          }
        }

        else
        {
          if (prevIsX)
          {
            String numremoved = pointname.substring(0, i - 1).concat(pointname.substring(i, pointname.length()));
            return (nameMatch(xmlname, pointname, i) || nameMatch(xmlname, original, i + adjust) || nameMatch(xmlname, numremoved, i - 1));
          }
        }
      }
    }

    return false;
  }

  private void removeExts(BComponent curComp) throws Exception
  {
    curComp.lease();
    station.lease();
    try
    {
      if (doDel)
      {
        //Delete History From Database Here---
        BOrd bord = BOrd.make("history:");   
        BFoxHistorySpace  c = (BFoxHistorySpace) bord.resolve(station).get();
        BHistoryDevice[] devices = c.listDevices();
        BIHistory[] hists = null;

        for (int i = 0; i < devices.length ; i++)
        {
          hists = c.listHistories(devices[i]);
          for (int j = 0; j < hists.length ; j++)
            System.out.println("BIHistory for Device: " + devices[i].getDeviceName() + " is: " + hists[j].getDisplayName(null));
        }
        for (int i = 0; i < hists.length; i++)
        {
          if(c.getHistory(hists[i].getId()).getDisplayName(null).indexOf("AuditHistory") < 0 && c.getHistory(hists[i].getId()).getDisplayName(null).indexOf("LogHistory") < 0)
            c.deleteHistory(hists[i].getId(), null);
        }
        //--------------------------
        curComp.removeAll();
      }

      else
      {
        BComponent[] children = curComp.getChildComponents();
        for(int i = 0; i < children.length; i++)
        {
          if(((BComponent) children[i]).getTypeDisplayName(null).indexOf("History Ext") >= 0)
          {
            //Delete History From Database Here---
            BHistoryExt hist = (BHistoryExt) children[i];
            hist.lease();
            BHistoryConfig histtwo = hist.getHistoryConfig();
            histtwo.lease();
            BHistoryId id = histtwo.getId();
            BOrd bord = BOrd.make("history:");  
            BFoxHistorySpace  c = (BFoxHistorySpace)bord.resolve(station).get();
            if(c.getHistory(id) != null)
              c.deleteHistory(id, null);
            //--------------------------
          }
        }
        curComp.removeAll();
      }
    } catch (Exception e)
    {
      System.out.println("   Error while removing exts:");   
      System.out.println(e.toString());       
    }
  }

  private BFacets getFacet(String facets, String precision)
  {
    facets = facets.toLowerCase();
    /*
     * Add String to Facets Decoder Here
     * Customize for TCC
     * Easy to Implement More
     */
    if(facets.indexOf("/") >= 0)
    {
      String[] texts = facets.split("/");
      return BFacets.make(BFacets.FALSE_TEXT, BString.make(texts[0]), BFacets.TRUE_TEXT, BString.make(texts[1]));
    }

    if(facets.indexOf("seconds") >= 0 && Integer.parseInt(precision) >= 0)
      return BFacets.make(BFacets.UNITS, BUnit.getUnit("second"), BFacets.PRECISION, BInteger.make(precision));

    if(facets.indexOf("in.w.c.") >= 0 && Integer.parseInt(precision) >= 0)
      return BFacets.make(BFacets.UNITS, BUnit.getUnit("inches of water"), BFacets.PRECISION, BInteger.make(precision));

    if(facets.indexOf("cfm") >= 0 && Integer.parseInt(precision) >= 0)
      return BFacets.make(BFacets.UNITS, BUnit.getUnit("cubic feet per minute"), BFacets.PRECISION, BInteger.make(precision));

    if(facets.indexOf("%rh") >= 0 && Integer.parseInt(precision) >= 0)
      return BFacets.make(BFacets.UNITS, BUnit.getUnit("percent relative humidity"), BFacets.PRECISION, BInteger.make(precision));

    if(facets.indexOf("%") >= 0 && Integer.parseInt(precision) >= 0)
      return BFacets.make(BFacets.UNITS, BUnit.getUnit("percent"), BFacets.PRECISION, BInteger.make(precision));

    if(facets.indexOf("psig") >= 0 && Integer.parseInt(precision) >= 0)
      return BFacets.make(BFacets.UNITS, BUnit.getUnit("pounds per square in guage"), BFacets.PRECISION, BInteger.make(precision));

    if(facets.indexOf("deg f per hour") >= 0 && Integer.parseInt(precision) >= 0)
      return BFacets.make(BFacets.UNITS, BUnit.getUnit("fahrenheit degrees"), BFacets.PRECISION, BInteger.make(precision));

    if(facets.indexOf("null") >= 0 && Integer.parseInt(precision) >= 0)
      return BFacets.make(BFacets.UNITS, BUnit.getUnit("null"), BFacets.PRECISION, BInteger.make(precision));

    if(facets.indexOf("btu/hr") >= 0 && Integer.parseInt(precision) >= 0)
      return BFacets.make(BFacets.UNITS, BUnit.getUnit("btus per hour"), BFacets.PRECISION, BInteger.make(precision));

    if(facets.indexOf("amps") >= 0 && Integer.parseInt(precision) >= 0)
      return BFacets.make(BFacets.UNITS, BUnit.getUnit("ampere"), BFacets.PRECISION, BInteger.make(precision));

    if(facets.indexOf("gpm") >= 0 && Integer.parseInt(precision) >= 0)
      return BFacets.make(BFacets.UNITS, BUnit.getUnit("gallons per minute"), BFacets.PRECISION, BInteger.make(precision));

    if(facets.indexOf("seconds") >= 0 && Integer.parseInt(precision) >= 0)
      return BFacets.make(BFacets.UNITS, BUnit.getUnit("second"), BFacets.PRECISION, BInteger.make(precision));

    if(facets.indexOf("kwh") >= 0 && Integer.parseInt(precision) >= 0)
      return BFacets.make(BFacets.UNITS, BUnit.getUnit("kilowatt hour"), BFacets.PRECISION, BInteger.make(precision));

    if(facets.indexOf("kw") >= 0 && Integer.parseInt(precision) >= 0)
      return BFacets.make(BFacets.UNITS, BUnit.getUnit("kilowatt"), BFacets.PRECISION, BInteger.make(precision));

    if(facets.indexOf("cfh") >= 0 && Integer.parseInt(precision) >= 0)
      return BFacets.make(BFacets.UNITS, BUnit.getUnit(""), BFacets.PRECISION, BInteger.make(precision));

    if(facets.indexOf("volts") >= 0 && Integer.parseInt(precision) >= 0)
      return BFacets.make(BFacets.UNITS, BUnit.getUnit("volt"), BFacets.PRECISION, BInteger.make(precision));

    if(facets.indexOf("mph") >= 0 && Integer.parseInt(precision) >= 0)
      return BFacets.make(BFacets.UNITS, BUnit.getUnit("miles per hour"), BFacets.PRECISION, BInteger.make(precision));

    if(facets.indexOf(";") >= 0 && Integer.parseInt(precision) >= 0)
    {
      String[] toMake = facets.split(";");
      String[] strings = new String[toMake.length];
      int[] ints = new int[toMake.length];
      int faults = 0 ;

      if(toMake.length == 0) return null;

      else if(toMake.length > 0)
      {
        if(toMake[0].indexOf("=") >= 0 && facets.split("=").length == toMake.length + 1)
        {
          for(int i = 0; i < toMake.length; i++)
          {
            String[] parts = toMake[i].split("=");
            faults = 0;
            try
            {
              ints[i] = Integer.parseInt(parts[0].trim());
              strings[i] = parts[1].trim();
            } catch (Exception e) {faults++;}

            try
            {
              ints[i] = Integer.parseInt(parts[1].trim());
              strings[i] = parts[0].trim();
            } catch (Exception e) {faults++;}

            if (faults == 2) return null;
          }
          return BFacets.makeEnum(BEnumRange.make(ints, strings));
        }
        else if(toMake[0].indexOf("=") == -1)
        {
          for(int i = 0; i < toMake.length; i++)
          {
            ints[i] = i;
            strings[i] = toMake[i].trim();
          }
          return BFacets.makeEnum(BEnumRange.make(ints, strings));
        }
      }
    }

    if(facets.indexOf("f") >= 0 && Integer.parseInt(precision) >= 0)
      return BFacets.make(BFacets.UNITS, BUnit.getUnit("fahrenheit"), BFacets.PRECISION, BInteger.make(precision));

    return null;
  }

  public void makeHistExt(BComponent point, int index, String type) throws Exception
  {
    String name = "";
    NodeList extList = ((Element) list.item(index)).getElementsByTagName("hist");

    if (extList.getLength() > 0)
    {
      NodeList bits = extList.item(0).getChildNodes();

      String trend = "";
      String facet = "";
      String precision = "";
      long inter = 0;
      int capacity = 0;
      BHistoryExt newHist;
      BHistoryConfig config = new BHistoryConfig();
      config.setSystemTags(BNameList.make("Archived"));

      for (int i = 0; i < bits.getLength(); i++)
      {
        if (bits.item(i).getNodeName().equals("trend"))
        {
          trend = ((Element) bits.item(i)).getAttribute("type").toLowerCase();
          if (trend.equals("int") && !((Element) bits.item(i)).getAttribute("v").isEmpty())
            inter = Integer.parseInt(((Element) bits.item(i)).getAttribute("v")) * 60000;
          else if(type.indexOf("numeric") >= 0 && !((Element) bits.item(i)).getAttribute("v").isEmpty()){
            inter = Integer.parseInt(((Element) bits.item(i)).getAttribute("v"));
          }
        }

        else if (bits.item(i).getNodeName().equals("cap"))
          capacity = Integer.parseInt(((Element) bits.item(i)).getAttribute("v"));

        else if (bits.item(i).getNodeName().equals("facets"))
          facet = ((Element) bits.item(i)).getAttribute("v");
        
        else if (bits.item(i).getNodeName().equals("precision"))
          precision = ((Element) bits.item(i)).getAttribute("v");
      }

      if (capacity != 0 && inter == 0)
        inter = (logPer / capacity);

      else if (capacity == 0 && inter != 0)
        capacity = (int) (logPer / (inter));

      //      int num = 0;
      //      while(parent.getTypeDisplayName(null).indexOf("Device")!=parent.getTypeDisplayName(null).length()-6){
      //        num++;
      //        System.out.println(parent.getTypeDisplayName(null));
      //        histName = histName.concat("parent.");
      //        try{
      //          parent = parent.getParent().asComponent();
      //        }catch(NullPointerException e){
      //          System.out.println("Point found with no parent device.");
      //          return;
      //        }
      //        if(parent==null||num==5){
      //          System.out.println("Point found with no parent device.");
      //          return;
      //        }
      //      }
      //histName = histName.concat("displayName%_%parent.displayName%");
      //setSourceName(BFormat.make(histName));

      if (type.indexOf("numeric") >= 0)
      {
        if (trend.equals("int"))
        {
          name = "NumericInterval";
          newHist = new BNumericIntervalHistoryExt();

          ((BNumericIntervalHistoryExt) newHist).setInterval(BRelTime.make(inter));
          config.setCapacity(BCapacity.makeByRecordCount(capacity));
          newHist.setHistoryName(getSourceName());
          newHist.setHistoryNameFormat(getSourceName());
          newHist.setHistoryConfig(config);

          BRolloverValue high = new BRolloverValue();
          BRolloverValue low = new BRolloverValue();
          high.setUnspecified(true);
          low.setUnspecified(true);
          
          ((BNumericIntervalHistoryExt) newHist).setMaxRolloverValue(high);
          ((BNumericIntervalHistoryExt) newHist).setMinRolloverValue(low);
        }

        else if (trend.equals("cov"))
        {
          name = "NumericCov";
          newHist = new BNumericCovHistoryExt();

          ((BNumericCovHistoryExt) newHist).setChangeTolerance(inter);
          config.setCapacity(BCapacity.makeByRecordCount(capacity));
          newHist.setHistoryName(getSourceName());
          newHist.setHistoryNameFormat(getSourceName());
          newHist.setHistoryConfig(config);
        }

        else
          newHist = null;
      }

      else if (type.indexOf("boolean") >= 0)
      {
        if (trend.equals("int"))
        {
          name = "BooleanInterval";
          newHist = new BBooleanIntervalHistoryExt();

          ((BBooleanIntervalHistoryExt) newHist).setInterval(BRelTime.make((long) inter));
          config.setCapacity(BCapacity.makeByRecordCount(capacity));
          newHist.setHistoryName(getSourceName());
          newHist.setHistoryNameFormat(getSourceName());
          newHist.setHistoryConfig(config);
        }

        else if (trend.equals("cov"))
        {
          name = "BooleanCov";
          newHist = new BBooleanCovHistoryExt();

          config.setCapacity(BCapacity.makeByRecordCount(capacity));
          newHist.setHistoryName(getSourceName());
          newHist.setHistoryNameFormat(getSourceName());
          newHist.setHistoryConfig(config);
        }

        else
          newHist = null;
      }

      else if (type.indexOf("enum") >= 0)
      {
        if (trend.equals("int"))
        {
          name = "EnumInterval";
          newHist = new BEnumIntervalHistoryExt();

          ((BEnumIntervalHistoryExt) newHist).setInterval(BRelTime.make((long) inter));
          config.setCapacity(BCapacity.makeByRecordCount(capacity));
          newHist.setHistoryName(getSourceName());
          newHist.setHistoryNameFormat(getSourceName());
          newHist.setHistoryConfig(config);
        }

        else if (trend.equals("cov"))
        {
          name = "EnumCov";
          newHist = new BEnumCovHistoryExt();

          config.setCapacity(BCapacity.makeByRecordCount(capacity));
          newHist.setHistoryName(getSourceName());
          newHist.setHistoryNameFormat(getSourceName());
          newHist.setHistoryConfig(config);
        }

        else
          newHist = null;
      }

      else
        newHist = null;

      facets[index] = getFacet(facet, precision);
      histExts[index] = newHist;
    }

    if (index >= 0 && histExts[index] != null)
    {
      BHistoryExt temp = (BHistoryExt) histExts[index].newCopy();

      if (point.get(name) != null)
        point.remove(name);

      temp.setEnabled(true);
      System.out.println("    Making History Ext for " + point.getDisplayName(null));
      point.add(name, temp);
      if(facets[index] != null)
      {
        System.out.println("    Making Facets for " + point.getDisplayName(null));
        ((BHistoryExt) point.get(name)).getHistoryConfig().set("valueFacets", facets[index]);
      }
    }
  }

  public void makeAlrmExt(BComponent point, int index, String type) throws Exception
  {
    String name = "";
    NodeList extList = ((Element) list.item(index)).getElementsByTagName("alrm");

    if (extList.getLength() > 0)
    {
      NodeList bits = extList.item(0).getChildNodes();

      BAlarmSourceExt newAlrm = new BAlarmSourceExt();
      BOffnormalAlgorithm alg;
      String extType = "";
      long delay = -1;
      double high = -9999;
      double low = -9999;
      double deadband = -9999;
      boolean val = true;
      String enumVal = "";
      String[] vals = new String[0];
      String highLim = null;
      String lowLim = null;
      String aClass = null;
      String offNorm = null;

      for (int i = 0; i < bits.getLength(); i++)
      {
        if (bits.item(i).getNodeName().equals("type"))
          extType = ((Element) bits.item(i)).getAttribute("t").toLowerCase();

        else if (bits.item(i).getNodeName().equals("val"))
        {
          String temp = ((Element) bits.item(i)).getAttribute("v");
          if (temp != null && temp.length() > 0)
            vals = temp.split(";");
          try
          {
            enumVal = ((Element) bits.item(i)).getAttribute("v");
          } catch (Exception e) {}
          val = ((Element) bits.item(i)).getAttribute("v").toLowerCase().equals("true");
        }

        else if (bits.item(i).getNodeName().equals("vals"))
        {
          String temp = ((Element) bits.item(i)).getAttribute("v");
          if (temp != null && temp.length() > 0)
            vals = temp.split(";");
        }

        else if (bits.item(i).getNodeName().equals("eval"))
          enumVal = ((Element) bits.item(i)).getAttribute("v");

        else if (bits.item(i).getNodeName().equals("high"))
        {
          high = Integer.parseInt(((Element) bits.item(i)).getAttribute("v"));
          highLim = ((Element) bits.item(i)).getAttribute("text");
        }

        else if (bits.item(i).getNodeName().equals("low"))
        {
          low = Integer.parseInt(((Element) bits.item(i)).getAttribute("v"));
          lowLim = ((Element) bits.item(i)).getAttribute("text");
        }

        else if (bits.item(i).getNodeName().equals("deadband"))
          deadband = Integer.parseInt(((Element) bits.item(i)).getAttribute("v"));
        
        else if (bits.item(i).getNodeName().equals("offnorm"))
          offNorm = ((Element) bits.item(i)).getAttribute("text");
        
        else if (bits.item(i).getNodeName().equals("delay"))
          delay = (long) Double.parseDouble(((Element) bits.item(i)).getAttribute("v")) * 60000;
        
        else if (bits.item(i).getNodeName().equals("class"))
          aClass = ((Element) bits.item(i)).getAttribute("c");
      }

      if (delay != -1)
        newAlrm.setTimeDelay(BRelTime.make(delay));

      //      int num = 0;
      //      while(parent.getTypeDisplayName(null).indexOf("Device")!=parent.getTypeDisplayName(null).length()-6){
      //        num++;
      //        System.out.println(parent.getTypeDisplayName(null));
      //        alrmName = alrmName.concat("parent.");
      //        try{
      //          parent = parent.getParent().asComponent();
      //        }catch(NullPointerException e){
      //          System.out.println("Point found with no parent device.");
      //          return;
      //        }
      //        if(parent==null||num==5){
      //          System.out.println("Point found with no parent device.");
      //          return;
      //        }
      //      }
      //alrmName = alrmName.concat("displayName%_%parent.displayName%");
      //setSourceName(BFormat.make(alrmName));

      newAlrm.setSourceName(getSourceName());
      newAlrm.setToOffnormalText(BFormat.make("%alarmData.sourceName%" + ": " + offNorm));
      newAlrm.setToNormalText(BFormat.make("%alarmData.sourceName% has returned to a normal value."));
      newAlrm.setAlarmEnable(BAlarmTransitionBits.make(BAlarmTransitionBits.TO_OFFNORMAL));
      newAlrm.setAlarmClass(aClass);

      if (type.indexOf("numeric") >= 0)
      {
        name = "OutOfRangeAlarmExt";

        alg = new BOutOfRangeAlgorithm();
        BLimitEnable limE = new BLimitEnable();

        if (high != -9999)
        {
          ((BOutOfRangeAlgorithm) alg).setHighLimit(high);
          ((BOutOfRangeAlgorithm) alg).setHighLimitText(BFormat.make(highLim));
          limE.setHighLimitEnable(true);
        }

        if (low != -9999)
        {
          ((BOutOfRangeAlgorithm) alg).setLowLimit(low);
          ((BOutOfRangeAlgorithm) alg).setLowLimitText(BFormat.make(lowLim));
          limE.setLowLimitEnable(true);
        }

        if (deadband != -9999)
          ((BOutOfRangeAlgorithm) alg).setDeadband(deadband);

        else
          ((BOutOfRangeAlgorithm) alg).setDeadband(0);

        ((BOutOfRangeAlgorithm) alg).setLimitEnable(limE);

        newAlrm.setOffnormalAlgorithm(alg);
      }

      else if (type.indexOf("boolean") >= 0)
      {
        if (extType.equals("cos"))
        {
          name = "BooleanChangeOfStateAlarmExt";
          alg = new BBooleanChangeOfStateAlgorithm();
          ((BBooleanChangeOfStateAlgorithm) alg).setAlarmValue(val);
        }

        else if (extType.equals("cf"))
        {
          name = "BooleanCommandFailureAlarmExt";
          alg = new BBooleanCommandFailureAlgorithm();
          ((BBooleanCommandFailureAlgorithm) alg).setFeedbackValue(new BStatusBoolean(val));
        }

        else
          alg = null;

        if (alg != null)
          newAlrm.setOffnormalAlgorithm(alg);
      }

      else if (type.indexOf("enum") >= 0)
      {
        if (extType.equals("cos"))
        {
          name = "EnumChangeOfStateAlarmExt";
          alg = new BEnumChangeOfStateAlgorithm();

          if (vals.length > 0)
          {
            BEnumRange opts = (BEnumRange) ((BEnumPoint) point).getFacets().getFacet("range");
            int[] ptords = opts.getOrdinals();
            ArrayList aTags = new ArrayList(vals.length);
            ArrayList ordinals = new ArrayList(vals.length);
            for (int i = 0; i < vals.length; i++)
            {
              for(int j = 0 ; j < ptords.length ; j++){
                if (opts.get(ptords[j]).getTag().toLowerCase().equals(vals[i].toLowerCase()))
                {
                  aTags.add(opts.get(ptords[j]).getTag());
                  ordinals.add(Integer.valueOf(opts.tagToOrdinal(opts.get(ptords[j]).getTag())));
                }
              }
            }

            int[] ords = new int[ordinals.size()];
            String[] tags = new String[aTags.size()];
            for(int i = 0; i < ordinals.size(); i++)
            {
              ords[i] = ((Integer) (ordinals.get(i))).intValue();
              tags[i] = ((String) (aTags.get(i))).toString();
            }

            ((BEnumChangeOfStateAlgorithm) alg).setAlarmValues(BEnumRange.make(ords, tags));
          }
        }

        else if (extType.equals("cf"))
        {
          name = "EnumCommandFailureAlarmExt";
          alg = new BEnumCommandFailureAlgorithm();

          BEnumRange opts = (BEnumRange) ((BEnumPoint) point).getFacets().getFacet("range");
          int[] ptords = opts.getOrdinals();

          for(int j = 0 ; j < ptords.length ; j++)
          {
            if (opts.get(ptords[j]).getTag().toLowerCase().equals(enumVal.toLowerCase()))
              ((BEnumCommandFailureAlgorithm) alg).setFeedbackValue(new BStatusEnum(BDynamicEnum.make(ptords[j])));
          }
        }

        else
          alg = null;

        if (alg != null)
          newAlrm.setOffnormalAlgorithm(alg);
      }

      else
        newAlrm = null;

      alrmExts[index] = newAlrm;
    }

    if (index >= 0 && alrmExts[index] != null && !name.equals(""))
    {
      System.out.println("    Making Alarm Ext for " + point.getDisplayName(null));
      if (point.get(name) != null)
        name = name.concat("1");
      
      point.add(name, alrmExts[index].newCopy());
    }
  }
  
  private boolean findDigit(String str)
  {
    boolean containsDigit = false;

    if (str != null)
    {
      char[] stra = str.toCharArray();
      for (int i = 0; i < stra.length; i++)
      {
          if (containsDigit = Character.isDigit(stra[i])) break;
      }
    }

    return containsDigit;
  }

  private BFoxProxySession openSession(String ipAddress, String port, String username, String password) throws Exception
  {
    try
    {
      System.out.print("Opening session on " + ipAddress + ":" + port + " as " + username + "... ");
      BFoxProxySession session = BFoxProxySession.make((BHost)BOrd.make("ip:" + ipAddress).get(), Integer.parseInt(port), username, password);
      System.out.println("Engaging session... ");
      session.engageNoRetry("session");
      return session;
    }
    catch (Exception e)
    {
      System.out.println("Fatal Error in openSession().");
      System.out.println(e.toString());
      return null;
    }
  }

  private void closeSession(BFoxProxySession session) throws Exception
  {
    System.out.println();
    try
    {
      System.out.print("Close session... "); 
      session.disengage("session");
    }
    catch (Exception e)
    {
      System.out.println("Fatal Error in closeSession().");   
      System.out.println(e.toString()); 
    }
    System.out.println("Done");
  }

  public BIcon getIcon() { return icon; }
  private static final BIcon icon = BIcon.make("local:|module://muninn/org/muninn/niagara/graphics/MuninnGroup.PNG");

  public static final Type TYPE = Sys.loadType(BExtService.class);
  public Type[] getServiceTypes() { return new Type[] { TYPE }; }
  public Type getType() { return TYPE; }

  public final synchronized void serviceStarted() throws Exception { INSTANCE = this; }

  public final synchronized void serviceStopped() throws Exception { INSTANCE = null; }

  public static final BExtService getInstance() { return INSTANCE; }
  
  private class Converter
  {
    ArrayList points;
    DocumentBuilderFactory docFactory;
    DocumentBuilder docBuilder;
    Document doc;

    private int itag;
    //private int iprefix;
    //private int isuffix;
    private int ifacet;
    private int idec;
    private int ipointtype;
    private int ilolim;
    private int ihilim;
    private int ideadband;
    private int itrend;
    private int itype;
    private int iparam;
    private int icapacity;
    private int ialarm;
    private int ialrmtype;
    private int ialrmval;
    private int ialrmdelay;
    private int ialrmclass;
    private int ilotxt;
    private int ihitxt;
    private int ioffnorm;

    Converter() {}

    private void run(String s) throws IOException
    {
      docFactory = DocumentBuilderFactory.newInstance();
      try
      {
        docBuilder = docFactory.newDocumentBuilder();
      } catch (ParserConfigurationException e2) { e2.printStackTrace(); }
      doc = docBuilder.newDocument();

      BufferedReader br = null;
      points = new ArrayList(0);
      try
      {
        br = new BufferedReader(new FileReader(s));
      } catch (FileNotFoundException e1) { e1.printStackTrace(); }

      String sCurrentLine;
      sCurrentLine = br.readLine();
      String[] parts = sCurrentLine.split(",", -2);
      ArrayList list = new ArrayList();
      for(int i = 0; i < parts.length; i++)
        list.add(parts[i].toUpperCase());

      itag		      = list.indexOf("TAG");
      //iprefix       = list.indexOf("PREFIX");
      //isuffix       = list.indexOf("SUFFIX");
      ifacet 			  = list.indexOf("FACET");
      idec 			    = list.indexOf("DEC");
      ipointtype 	  = list.indexOf("TYPE");
      ilolim 			  = list.indexOf("LO LIM");
      ihilim 			  = list.indexOf("HI LIM");
      ideadband     = list.indexOf("DEADBAND");
      itrend 			  = list.indexOf("TREND");
      itype 		    = list.indexOf("HIST TYPE");
      iparam 			  = list.indexOf("PARM");
      icapacity     = list.indexOf("CAPACITY");
      ialarm 			  = list.indexOf("ALARM");
      ialrmtype	    = list.indexOf("ALARM TYPE");
      ialrmval		  = list.indexOf("VAL");
      ialrmdelay    = list.indexOf("DELAY");
      ialrmclass    = list.indexOf("CLASS");
      ilotxt 			  = list.indexOf("LO TXT");
      ihitxt 			  = list.indexOf("HI TXT");
      ioffnorm 		  = list.indexOf("OFF NORM TXT");

      sCurrentLine = br.readLine();
      while (sCurrentLine != null)
      {
        parse(sCurrentLine);
        sCurrentLine = br.readLine();
      }
      if (br != null) br.close();

      try
      {
        Element rootElement = doc.createElement("tag");
        doc.appendChild(rootElement);

        for(int i = 0; i < points.size(); i++)
          rootElement.appendChild((Element) points.get(i));

        // write the contents into xml file
        TransformerFactory transformerFactory = TransformerFactory.newInstance();
        Transformer transformer = transformerFactory.newTransformer();
        transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "2");
        transformer.setOutputProperty(OutputKeys.INDENT, "yes");
        DOMSource source = new DOMSource(doc);
        
        String dir = s.substring(0, s.lastIndexOf('\\') + 1);
        
        StreamResult result = new StreamResult(new File(dir + "ExtList.xml"));

        transformer.transform(source, result);

        System.out.print("Saving... ");

      } catch (TransformerException tfe) { tfe.printStackTrace(); }
    }

    private void parse(String sCurrentLine)
    {
      String[] parts = sCurrentLine.split(",", -2);
      if (parts.length - 1 == sCurrentLine.length()) return;

      String stag = "";
      //String sprefix = "";
      //String ssuffix = "";
      String sfacet = "";
      String sdec = "";
      String spointtype = "";
      String slolim = "";
      String shilim = "";
      String sdeadband = "";
      String strend = "";
      String stype = "";
      String sparam = "";
      String scapacity = "";
      String salarm = "";
      String salrmtype = "";
      String salrmval = "";
      String salrmdelay = "";
      String salrmclass = "";
      String slotxt = "";
      String shitxt = "";
      String soffnorm = "";

      try { if (itag >= 0)         { stag = parts[itag]; } }                           catch (Exception e) {}
      //try { if (iprefix >= 0)      { sprefix = parts[iprefix]; } }                     catch (Exception e) {}
      //try { if (isuffix >= 0)      { ssuffix = parts[isuffix]; } }                     catch (Exception e) {}
      try { if (ifacet >= 0)       { sfacet = parts[ifacet]; } }                       catch (Exception e) {}
      try { if (idec >= 0)         { sdec = parts[idec]; } }                           catch (Exception e) {}
      try { if (ipointtype >= 0)   { spointtype = parts[ipointtype].toUpperCase(); } } catch (Exception e) {}
      try { if (ilolim >= 0)       { slolim = parts[ilolim]; } }                       catch (Exception e) {}
      try { if (ihilim >= 0)       { shilim = parts[ihilim]; } }                       catch (Exception e) {}
      try { if (ideadband >= 0)    { sdeadband = parts[ideadband];} }                  catch (Exception e) {}
      try { if (itrend >= 0)       { strend = parts[itrend].toUpperCase(); } }         catch (Exception e) {}
      try { if (itype >= 0)        { stype = parts[itype].toUpperCase(); } }           catch (Exception e) {}
      try { if (iparam >= 0)       { sparam = parts[iparam]; } }                       catch (Exception e) {}
      try { if (icapacity >= 0)    { scapacity = parts[icapacity]; } }                 catch (Exception e) {}
      try { if (ialarm >= 0)       { salarm = parts[ialarm].toUpperCase(); } }         catch (Exception e) {}
      try { if (ialrmtype >= 0)    { salrmtype = parts[ialrmtype].toLowerCase(); } }   catch (Exception e) {}
      try { if (ialrmval >= 0)     { salrmval = parts[ialrmval]; } }                   catch (Exception e) {}
      try { if (ialrmdelay >= 0)   { salrmdelay = parts[ialrmdelay]; } }               catch (Exception e) {}
      try { if (ialrmclass >= 0)   { salrmclass = parts[ialrmclass]; } }               catch (Exception e) {}
      try { if (ilotxt >= 0)       { slotxt = parts[ilotxt]; } }                       catch (Exception e) {}
      try { if (ihitxt >= 0)       { shitxt = parts[ihitxt]; } }                       catch (Exception e) {}
      try { if (ioffnorm >= 0)     { soffnorm = parts[ioffnorm]; } }                   catch (Exception e) {}


      if (stag.equals("")) return;
      Element point = doc.createElement("point");
      point.setAttribute("name", stag);
      //point.setAttribute("prefix", sprefix);
      //point.setAttribute("suffix", ssuffix);

      Element hist = doc.createElement("hist");
      Element alrm = doc.createElement("alrm");

      boolean goodhist = true;

      if (strend.indexOf("X") >= 0)
      {
        Element etrend = doc.createElement("trend");
        if (stype.indexOf("I") >= 0)
        {
          try
          {
            if ((sparam.isEmpty() && scapacity.isEmpty()))
              goodhist = false;
          } catch (Exception e) { goodhist = false; }
          etrend.setAttribute("type", "int");
          etrend.setAttribute("v", sparam);
        }

        else if (stype.indexOf("C") >= 0)
        {
          try
          {
            if (scapacity.isEmpty() || (sparam.isEmpty() && spointtype.indexOf('N') >= 0))
              goodhist = false;
          } catch (Exception e) { goodhist = false; }
          etrend.setAttribute("type", "cov");
          etrend.setAttribute("v", sparam);
        }

        else
          goodhist = false;

        Element cap = doc.createElement("cap");
        cap.setAttribute("v", scapacity);
        Element facet = doc.createElement("facets");
        facet.setAttribute("v", sfacet);
        Element precision = doc.createElement("precision");
        precision.setAttribute("v", sdec);
        hist.appendChild(etrend);

        if (!scapacity.isEmpty())
          hist.appendChild(cap);

        if (!sfacet.isEmpty())
          hist.appendChild(facet);

        if (!sdec.isEmpty())
          hist.appendChild(precision);

        if (goodhist)
          point.appendChild(hist);
      }
      //--------------------------------------------------
      //----------ALARMS----------------------------------
      boolean goodalarm = true;
      if (salarm.indexOf("X") >= 0)
      {
        if (spointtype.toLowerCase().indexOf("b") >= 0)
        {
          if (!salrmtype.equals("cos") && !salrmtype.equals("cf"))
            goodalarm = false;

          Element etype = doc.createElement("type");
          etype.setAttribute("t", salrmtype);
          Element eval = doc.createElement("val");
          eval.setAttribute("v", salrmval);
          alrm.appendChild(etype);
          alrm.appendChild(eval);
        }
        else if (spointtype.toLowerCase().indexOf("e") >= 0)
        {
          if ((!salrmtype.equals("cos") && !salrmtype.equals("cf")))
            goodalarm = false;

          if (salrmtype.equals("cos"))
          {
            String[] vals = salrmval.split(";");
            for (int i = 0; i < vals.length; i++)
            {
              if(sfacet.indexOf(vals[i].trim())<0)
                goodalarm = false;
            }
          }
          Element etype = doc.createElement("type");
          etype.setAttribute("t", salrmtype);
          Element eval = doc.createElement("val");
          eval.setAttribute("v", salrmval);
          alrm.appendChild(etype);
          alrm.appendChild(eval);
        }
        else if (spointtype.toLowerCase().indexOf("n") >= 0 && !(shilim.isEmpty() || slolim.isEmpty()))
        {
          Element ehigh = doc.createElement("high");
          ehigh.setAttribute("v", shilim);
          ehigh.setAttribute("text", shitxt);
          Element elow = doc.createElement("low");
          elow.setAttribute("v", slolim);
          elow.setAttribute("text", slotxt);
          alrm.appendChild(elow);
          alrm.appendChild(ehigh);
        }
        else
          goodalarm = false;
        
        Element edelay = doc.createElement("delay");
        edelay.setAttribute("v", salrmdelay);
        Element eclass = doc.createElement("class");
        eclass.setAttribute("c", salrmclass);
        Element eoffnorm = doc.createElement("offnorm");
        eoffnorm.setAttribute("text", soffnorm);
        Element deadband = doc.createElement("deadband");
        deadband.setAttribute("v", sdeadband);

        if (!sdeadband.isEmpty())
          alrm.appendChild(deadband);

        if (!salrmdelay.isEmpty())
          alrm.appendChild(edelay);

        else
        {
          edelay.setAttribute("v", "0");
          alrm.appendChild(edelay);
        }

        if (!salrmclass.isEmpty())
          alrm.appendChild(eclass);

        else
        {
          eclass.setAttribute("c", "DefaultAlarmClass");
          alrm.appendChild(eclass);
        }

        if (!soffnorm.isEmpty())
          alrm.appendChild(eoffnorm);

        else
        {
          eoffnorm.setAttribute("text", "has transitioned to offnormal value %out%.");
          alrm.appendChild(eoffnorm);
        }

        if (goodalarm)
          point.appendChild(alrm);
      }

      if (point.getChildNodes().getLength() > 0 && isValid(point))
        points.add(point);
    }

    private boolean isValid(Element point) 
    {
      return true;
    }
  }
}
